package request

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"github.com/golang-module/dongle"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	"hc_server/pkg/aes"
	rsa2 "hc_server/pkg/rsa"
	"io"
	"net/http"
	"sort"
	"strings"
	"time"
)

const (
	// AesPrivate AES密钥
	AesPrivate   = "QXDDA1P6PJSXAWSKZX38Z9J1PW6DANQJ"
	PartnerId    = "P0001137" //合作方ID
	AesSecretKey = "1122334455667788"
)

func QuickRequest(param map[string]interface{}, reqPath string) (interface{}, error) {
	temp := GetPublicParameter(param)
	logrus.Infof("请求接口: url:%s", reqPath)
	// 创建一个通道来接收返回的数据和错误
	resultChan := make(chan interface{}, 1)
	errChan := make(chan error, 1)

	// 启动协程执行请求
	go func() {
		postData, err := json.Marshal(temp)
		if err != nil {
			logrus.Errorf("请求接口出错: 序列化参数失败 %s", err.Error())
			errChan <- errors.New("请求接口出错")
			return
		}

		res, err := http.Post(reqPath, "application/json", bytes.NewBuffer(postData))
		if err != nil {
			logrus.Errorf("请求接口出错: %s", err.Error())
			errChan <- errors.New("请求接口出错")
			return
		}
		defer res.Body.Close() // 不需要再传入 Body 作为参数，直接调用 Close() 方法即可

		body, err := io.ReadAll(res.Body)
		if err != nil {
			logrus.Errorf("io.ReadAll出错: %s", err.Error())
			errChan <- errors.New("解析数据错误")
			return
		}

		var data map[string]interface{}
		err = json.Unmarshal(body, &data)
		if err != nil {
			logrus.Errorf("解析json出错: %s", err.Error())
			errChan <- errors.New("解析json出错")
			return
		}

		if cast.ToInt(data["codeService"]) != 0 {
			logrus.Errorf("请求接口出错: %s", data["msg"])
			errChan <- errors.New(cast.ToString(data["msg"]))
			return
		}

		//delete(temp, "sign")
		//verify := VerifySignature(temp, cast.ToString(data["sign"]))
		//if verify != true {
		//	logrus.Error("验证sign失败")
		//	errChan <- errors.New("验证失败")
		//	return
		//}

		plainText, err := aes.AesCbcDecryptByBase64(cast.ToString(data["data"]), []byte(AesPrivate), []byte(AesSecretKey))
		if err != nil {
			logrus.Errorf("aes解密失败: %v", err)
			errChan <- errors.New("验证失败")
			return
		}

		var tempResult map[string]interface{}
		err = json.Unmarshal(plainText, &tempResult)
		if err != nil {
			logrus.Errorf("data数据转json失败: %v", err)
			errChan <- errors.New("请求失败")
			return
		}
		// 发送成功的结果到通道
		resultChan <- tempResult
	}()

	// 从通道中接收结果或错误
	select {
	case data := <-resultChan:
		return data, nil
	case err := <-errChan:
		return nil, err
	}
}

// GetPublicParameter 获取公共参数
func GetPublicParameter(mapd map[string]interface{}) map[string]string {
	// 将map转换为JSON格式的字符串
	jsonData, _ := json.Marshal(mapd)

	encryptedData, err := aes.AesCbcEncryptBase64(jsonData, []byte(AesPrivate), []byte(AesSecretKey))
	if err != nil {
		fmt.Println(err)
	}

	paramMap := map[string]string{}
	paramMap["partnerId"] = PartnerId                                                      //合作方ID
	paramMap["timestamp"] = cast.ToString(time.Now().UnixNano() / int64(time.Millisecond)) //业务请求时间戳
	paramMap["nonce"] = generateRandomString(12)                                           //长度为12 的随机字符串
	paramMap["data"] = encryptedData                                                       //加密后的数据

	withRSA, _ := generatePrivateSignature(paramMap, rsa2.PirvateKey)
	paramMap["sign"] = withRSA //签名

	return paramMap
}

// VerifySignature 验签函数
func VerifySignature(params map[string]string, expectedSignature string) bool {
	// 1. 将集合 M 内非空参数值的参数按照参数名 ASCII 码从小到大排序
	var keys []string
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 2. 使用 URL 键值对的格式拼接成字符串 stringA
	var stringA string
	for _, k := range keys {
		stringA += fmt.Sprintf("%s=%s&", k, params[k])
	}
	stringA = strings.TrimSuffix(stringA, "&")
	return RsaVerifySign([]byte(rsa2.LDPublicKey), crypto.SHA256, []byte(stringA), []byte(expectedSignature))
}

// 生成签名
func generatePrivateSignature(data map[string]string, privateKeyPEM string) (string, error) {
	// 1. 将集合 M 内非空参数值的参数按照参数名 ASCII 码从小到大排序
	var keys []string
	for k := range data {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 2. 使用 URL 键值对的格式拼接成字符串 stringA
	var stringA string
	for _, k := range keys {
		stringA += fmt.Sprintf("%s=%s&", k, data[k])
	}
	stringA = strings.TrimSuffix(stringA, "&")
	// 对字节切片进行 rsa 签名
	sign := dongle.Sign.FromBytes([]byte(stringA)).ByRsa([]byte(privateKeyPEM), dongle.SHA256)

	// 4. Base64 编码结果作为签名
	return sign.ToBase64String(), nil
}

func RsaVerifySign(pubkey []byte, hash crypto.Hash, data, sig []byte) bool {
	block, _ := pem.Decode(pubkey)
	if block == nil {
		return false
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return false
	}

	h := sha256.Sum256(data)
	hashed := h[:]
	if err = rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), hash, hashed, sig); err != nil {
		return false
	}
	return true
}

func generateRandomString(length int) string {
	randomBytes := make([]byte, length)
	_, err := rand.Read(randomBytes)
	if err != nil {
		return ""
	}

	randomString := base64.URLEncoding.EncodeToString(randomBytes)
	return randomString[:length]
}
